package cn.ges.movie.controller.movie

import cn.ges.date.DateUtil
import cn.ges.movie.config.aop.JsCastAop
import cn.ges.movie.entity.*
import cn.ges.movie.mapper.*
import cn.ges.movie.service.MovieService
import cn.ges.movie.vo.base.SelectLabel
import cn.ges.movie.vo.mv.*
import cn.ges.page.PageHelper
import cn.ges.random.RandomUtil
import cn.ges.redis.Lock
import cn.ges.redis.RedisUtil
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.baomidou.mybatisplus.extension.plugins.pagination.Page
import com.ges.common.anno.DataBuryPoint
import com.ges.common.anno.ParameterCheck
import com.ges.common.config.autowired.UserId
import com.ges.common.dto.MsgDTO
import com.ges.common.vo.ReturnJsonVO
import com.ges.common.vo.base.BasePageVO
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.bind.annotation.*
import java.time.Instant
import java.time.LocalDate
import javax.annotation.Resource

/**
 *
 * @author gespent@163.com
 * @date 2021/4/16 16:42
 */
@RestController
open class MovieController {
    @Resource
    lateinit var movieService: MovieService

    @Resource
    lateinit var schedulingMapper: MvSchedulingMapper

    @Resource
    lateinit var movieMapper: MovieMapper

    @Resource
    lateinit var mvTicketMapper: MvTicketMapper


    @Resource
    lateinit var movieHallMapper: MvMovieHallMapper

    @Resource
    lateinit var cinemaMapper: MvCinemaMapper


    @Resource
    lateinit var ticketMapper: MvTicketMapper

    companion object {
        const val TC = "LC:T_"
    }


    @DataBuryPoint("修改新增电影")
    @RequestMapping(value = ["/v1/admin/movie/mvm/"], method = [RequestMethod.POST])
    @ParameterCheck
    open fun addOrUpdateMvm(@RequestBody vo: MmnVO): ReturnJsonVO {
        return vo.doModify(false).cast4Return()
    }

    @DataBuryPoint("分页查询电影")
    @RequestMapping(value = ["/v1/admin/movie/mvm/"], method = [RequestMethod.GET])
    open fun listMvM(vo: BasePageVO): ReturnJsonVO {
        return ReturnJsonVO.Companion.success(
            movieMapper.selectPage(
                Page<Movie>(vo.current!!.toLong(), vo.size!!.toLong()), QueryWrapper<Movie>()
            )
        )
    }

    @DataBuryPoint("分页查询票务")
    @RequestMapping(value = ["/v1/admin/movie/amvt/"], method = [RequestMethod.GET])
    open fun listamvt(vo: MVTPageVO): ReturnJsonVO {
        return ReturnJsonVO.Companion.success(
            ticketMapper.selectPage(
                Page<MvTicket>(vo.current!!.toLong(), vo.size!!.toLong()), QueryWrapper<MvTicket>().apply {
                    if (vo.userId != null) {
                        // 用户ID
                        eq("user_id", vo.userId)
                        if (vo.mhvId != null) {
                            // 放映厅
                            eq("mvh_id", vo.mhvId)
                        }
                        if (vo.mhvSd != null) {
                            // 排期
                            eq("msc_id", vo.mhvSd)
                        }
                    }
                }
            )
        )
    }


    @DataBuryPoint("修改新增排期")
    @RequestMapping(value = ["/v1/admin/movie/amvD/"], method = [RequestMethod.POST])
    @ParameterCheck
    open fun addOrUpdateAmvD(@RequestBody vo: MmdVO): ReturnJsonVO {
        return vo.doModify(false).cast4Return()
    }

    @DataBuryPoint("分页查询电影排期")
    @RequestMapping(value = ["/v1/admin/movie/amvD/"], method = [RequestMethod.GET])
    open fun listAmvD(vo: BasePageVO): ReturnJsonVO {

        val date = schedulingMapper.selectPage(
            Page<MvScheduling>(vo.current!!.toLong(), vo.size!!.toLong()), QueryWrapper<MvScheduling>()
        )
        val arrayList = ArrayList<MvSchedulingVO>()
        date.records.forEach {
            arrayList.add(MvSchedulingVO.cast(it))
        }
        return ReturnJsonVO.Companion.success(
            JsCastAop.clonePage(date, arrayList)
        )
    }

    @DataBuryPoint("分页查询电影院")
    @RequestMapping(value = ["/v1/admin/movie/mvc/"], method = [RequestMethod.GET])
    open fun listMvc(vo: BasePageVO): ReturnJsonVO {
        return ReturnJsonVO.Companion.success(
            cinemaMapper.selectPage(
                Page<MvCinema>(vo.current!!.toLong(), vo.size!!.toLong()), QueryWrapper<MvCinema>()
            )
        )
    }

    @DataBuryPoint("修改新增电影院")
    @RequestMapping(value = ["/v1/admin/movie/mvc/"], method = [RequestMethod.POST])
    @ParameterCheck
    open fun addOrUpdateMmc(@RequestBody vo: MmcVO): ReturnJsonVO {
        return vo.doModify(false).cast4Return()
    }

    @DataBuryPoint("修改新增放映厅")
    @RequestMapping(value = ["/v1/admin/movie/ach/"], method = [RequestMethod.POST])
    @ParameterCheck
    open fun addOrUpdateAch(@RequestBody vo: AchVO): ReturnJsonVO {
        return vo.doModify(false).cast4Return()
    }

    @DataBuryPoint("分页查询放映厅")
    @RequestMapping(value = ["/v1/admin/movie/ach/"], method = [RequestMethod.GET])
    open fun listAch(vo: BasePageVO): ReturnJsonVO {
        return ReturnJsonVO.Companion.success(
            movieHallMapper.selectPage(
                Page<MvMovieHall>(vo.current!!.toLong(), vo.size!!.toLong()), QueryWrapper<MvMovieHall>()
            )
        )
    }

    @DataBuryPoint("获得影票二维码")
    @RequestMapping(value = ["/v1/movie/ticket/code/{id}/"], method = [RequestMethod.GET])
    open fun getTicketCode(@UserId userId: Long, @PathVariable id: Long): ReturnJsonVO {
        return movieService.getQcode(id, userId).cast4Return()
    }


    class InspectTicketVO() {
        var code: String? = null
    }

    @DataBuryPoint("验票")
    @RequestMapping(value = ["/v1/movie/ticket/inspect/"], method = [RequestMethod.POST])
    open fun inspectTicket(@UserId userId: Long, @RequestBody vo: InspectTicketVO): ReturnJsonVO {
        return movieService.inspectTicket(vo.code!!).cast4Return()
    }


    @DataBuryPoint("购买电影票")
    @RequestMapping(value = ["/v1/movie/buy/"], method = [RequestMethod.POST])
    @Transactional
    open fun buyTicket(@UserId userId: Long, @RequestBody vo: BuyTicket): ReturnJsonVO {


        val key = TC + vo.sdId
        val values = userId.toString()
        if (RedisUtil.sHasKey(key, values)) {
            return ReturnJsonVO.fail("您已经购买过该场电影")
        }else{
            // 获得锁
            Lock.lock(key,1000,1000,1).apply {
                if (this == 0L) {
                    return ReturnJsonVO.fail("请求过于频繁 请稍后再试")
                }
            }
        }

        val geMscById = movieService.geMscById(vo.sdId!!)!!
        val geMvhById = movieService.geMvhById(geMscById.mhId!!)!!

        // 新增一个redis 现在到结束时间的秒数
        val nowUntilSecond =
            cn.ges.date.DateUtil.nowUntilSecond(DateUtil.instantToLocalDateTime(geMscById.endTime!!))
        if (nowUntilSecond <= 0) {
            return ReturnJsonVO.fail("电影或已开场 请刷新后再试")
        }

        val sGetSetSize = RedisUtil.sGetSetSize(key)
        if (sGetSetSize >= geMvhById.maxCount!!) {
            return ReturnJsonVO.fail("本场电影票已购买完毕 请选择其他场次")
        }
        // 直接塞入
        RedisUtil.sSetAndTime(key, nowUntilSecond, values)

        MvTicket().apply {
            id = RandomUtil.getSequence()
            this.userId = userId
            mscId = vo.sdId
            tState = 0
            mvhId = geMscById.mhId
        }.run {
            if (mvTicketMapper.insert(this) == 0) {
                MsgDTO.setRollBackOnly()
                RedisUtil.setRemove(key, values)
                return ReturnJsonVO.fail("购买失败 请稍后重试")
            }
        }
        val sGetSetSize1 = RedisUtil.sGetSetSize(key)
        if (sGetSetSize1 >= geMvhById.maxCount!!) {
            // 购买完成后超限
            MsgDTO.setRollBackOnly()
            RedisUtil.setRemove(key, values)
            return ReturnJsonVO.fail("本场电影票已购买完毕 请选择其他场次")
        }
        return ReturnJsonVO.Companion.success("")
    }

    @DataBuryPoint("获得所有电影信息")
    @RequestMapping(value = ["/v1/movie/all/"], method = [RequestMethod.GET])
    open fun getAllMovie(): ReturnJsonVO {
        return ReturnJsonVO.Companion.success(movieService.listAllMovie())
    }


    @DataBuryPoint("初始化所有电影放映厅")
    @RequestMapping(value = ["/v1/movie/init/mh"], method = [RequestMethod.GET])
    open fun initHM(): ReturnJsonVO {
        return ReturnJsonVO.Companion.success(movieService.initMVH())
    }

    @DataBuryPoint("初始化某天排班")
    @RequestMapping(value = ["/v1/movie/init/date/mv"], method = [RequestMethod.GET])
    open fun initDmv(): ReturnJsonVO {
        return ReturnJsonVO.Companion.success(movieService.initMvByDate(LocalDate.now().plusDays(1)))
    }

    @DataBuryPoint("获得电影ID选择框信息")
    @RequestMapping(value = ["/v1/movie/label/"], method = [RequestMethod.GET])
    open fun getMovieInfoLabel(): ReturnJsonVO {
        val listAllMovie = movieService.listAllMovie()
        val arrayList = ArrayList<SelectLabel>()
        listAllMovie.forEach {
            arrayList.add(SelectLabel().apply {
                value = it.id!!.toString()
                label = it.name
            })
        }
        return ReturnJsonVO.Companion.success(HashMap<String, Any>().apply {
            this["count"] = arrayList.size
            this["list"] = arrayList
        })
    }

    @DataBuryPoint("获得用户电影票信息")
    @RequestMapping(value = ["/v1/movie/ticket/"], method = [RequestMethod.GET])
    open fun listUserTicket(@UserId userId: Long, vo: BasePageVO): ReturnJsonVO {
        val listUserTicket = movieService.listUserTicket(userId, vo.size!!, vo.current!!)
        val arrayList = ArrayList<HashMap<String, Any?>>()
        val now = Instant.now()

        listUserTicket.second.forEach {
            val hashMap = HashMap<String, Any?>()
            arrayList.add(hashMap)
            // 排期信息
            val geMscById = movieService.geMscById(it.mscId!!)!!
            // 电影信息
            val geMvById = movieService.geMvById(geMscById.mvId!!)!!
            // 电影厅信息
            val geMvhById = movieService.geMvhById(geMscById.mhId!!)!!
            // 电影院信息
            val cmById = movieService.getCmById(geMvhById.mcId!!)!!
            // 电影厅 名称
            hashMap["hName"] = "${geMvhById.name}"

            hashMap["tId"] = "${it.id}"
            // 电影名称
            hashMap["name"] = "《${geMvById.name}》"
            // 入场时间
            hashMap["startTime"] = DateUtil.instantToLocalDateTime(geMscById.startTime!!)
            hashMap["hasEnd"] = Instant.now() > geMscById.endTime
            // 购买价格
            hashMap["price"] = "￥${geMscById.price}"
            // 电影院名称
            hashMap["cmName"] = "${cmById.name}"
            // 电影院地址
            hashMap["address"] = "${cmById.address}"
            // 门票状态
            hashMap["tState"] = it.tState
            hashMap["state"] = if (geMscById.startTime!! <= now) {
                // 电影已经开始
                if (geMscById.endTime!! >= now) {
                    if (it.tState == 1) {
                        // 已验票
                        "电影已经开始播放-已验票 "
                    } else {
                        "电影已经开始播放-请尽快验票入场"
                    }
                } else {
                    "放映结束 电影票已过期"
                }
            } else {
                // 电影还未开始
                if (it.tState == 1) {
                    // 已验票
                    "电影还未开始-已验票 "
                } else {
                    "电影还未开始-等待验票"
                }
            }
        }
        return ReturnJsonVO.Companion.success(HashMap<String, Any>().apply {
            this["count"] = listUserTicket.first
            this["list"] = arrayList
        })
    }

    @DataBuryPoint("查询电影安排")
    @RequestMapping(value = ["/v1/movie/info/"], method = [RequestMethod.GET])
    @ParameterCheck
    open fun getMvInfo(@UserId userId: Long, vo: MovieQueryVO): ReturnJsonVO {

        val hashMap = HashMap<String, Any?>(2)
        val queryMovieInfo = movieMapper.queryMovieInfo(
            vo.longitude!!,
            vo.latitude!!,
            PageHelper.getActualLimitString(vo.size!!.toInt(), vo.current!!.toInt())!!,
            vo.startTme!!,
            vo.endTme!!,
            vo.mvId!!
        )
        movieService.geMvById(vo.mvId!!)?.let { movie ->
            queryMovieInfo.forEach {
                it.mvName = movie.name
                val key = TC + it.sdId
                it.hasBuy = RedisUtil.sHasKey(key, userId.toString())
                it.nowCount = RedisUtil.sGetSetSize(key).toInt()
            }
        }

        hashMap["records"] = queryMovieInfo
        if (queryMovieInfo.isNotEmpty()) {
            hashMap["total"] = movieMapper.queryMovieInfoCount(vo.startTme!!, vo.endTme!!, vo.mvId!!)
        }
        return ReturnJsonVO.Companion.success(hashMap)
    }

}